/**
 *   Copyright (C) 2012  Development Solutions (DS) Software
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *   The following additional terms will apply to the software provided with
 *   this file.
 *      1. Preserve the original authors’ name.
 *      2. Documenting all contributions and specify the contributor's name,
 *         the date of the contribution and some email to future contact.
 * 
 */

package org.dss.gil.view;

import java.util.ArrayList;
import java.util.Stack;
import org.dss.gil.util.SimTableModel;
import org.dss.gil.service.MessagesService;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableColumn;
import org.apache.log4j.Logger;
import org.dss.gil.util.Globals;
import org.dss.gil.controller.IdeMethods;
import org.dss.gil.interfaces.GenericFileInterface;
import org.dss.gil.service.ValidationService;

/** 
 * Esta clase representa a la tabla de programación en donde se escriben las simulaciones.
 * ----------------------
 * This class represents the programmation Table where we write the simulations.
 *  
 *  @author  Fernando Gil <lobho.gil@gmail.com>
 *  @since   September 2012
 *  @version 1.0
 */


public class TablePanel extends javax.swing.JPanel implements GenericFileInterface{

    private static Logger logger = Logger.getLogger(TablePanel.class.getName());
    
    /*********************************************************************
     ********************* E L E M E N T S *******************************
     *********************************************************************/
    private Object  obj_newRow [] = new Object [] {
                                    new Boolean(true), null , null, null, null }; //new Row
    public String [] str_columNames = {
                                        Globals.language.getMessage("label.col.active"),
                                        Globals.language.getMessage("label.col.label"),
                                        Globals.language.getMessage("label.col.block"),
                                        Globals.language.getMessage("label.col.value"),
                                        Globals.language.getMessage("label.col.comment")};  
    private MessagesService           mss_message;
    private ValidationService validations;
    private TableColumn        tbc_column;
    private SimTableModel      model;
    private ListSelectionModel lsm_list;
    private String             globalComments;
    private GlobalComments commetsWin;

    private final int ROW_HEIGHT = 25;
    private final int MAX_ROWS_TO_INSERT = 50;
    private final int MIN_ROWS_TO_INSERT = 1;

    /*********************************************************************
     *********************** POOL FOR UNDO & REDO ************************
     *********************************************************************/
    Stack <Object[]> undoPool, redoPool, pool;
    
    /** ACTIONS*/
    private final int INSERT    = 1;
    private final int DELETE    = -1;
    private final int MOVE_UP   = 2;
    private final int MOVE_DOWN = -2;
    private final int ACTIVE    = 3;
    private final int DEACTIVE  = -3;
    private final int CLEAR     = 4;
    private final int RESTORE   = -4;
    private final int MOVE_FIRST= 5;
    private final int RETURN1   = -5;
    private final int MOVE_LAST = 6;
    private final int RETURN2   = -6;

    /* Here we'll push the action in the pool. Always assure maintain the desired stack size.
     * INSERT, DELETE, CLEAR save the location of the row inserted/removed and the data of every row.
     * ACTIVE and DEACTIVE save the location of the modified rows.
     * MOVE_UP AND DOWN save the new location of rows.
     * MOVE_FIRST and MOVE_LAST save the original location of rows, before the movement, and the data.
     *  */
    boolean executingUndo=false, executingRedo=false;
    private void setInUndoPool(int action, int firstIndex, int lastIndex, Object[][] data) {
        if(!executingUndo){
            Object[] obj = new Object[]{action,firstIndex,lastIndex,data};
            if(undoPool.size()== Globals.poolSize)undoPool.remove(0);
            undoPool.push(obj);
            ide_methods.setUndoButtonsEnable(true);
            logger.debug("**UNDO**: "+action+"**"+firstIndex+"**"+lastIndex);
            if(!executingRedo){
                redoPool = new Stack();
                ide_methods.setRedoButtonsEnable(false);
            }
        }else{
            this.setInRedoPool(action, firstIndex, lastIndex, data);
        }
    }
    private void setInRedoPool(int action, int firstIndex, int lastIndex, Object[][] data) {
        if(executingUndo){
            Object[] obj = new Object[]{action,firstIndex,lastIndex,data};
            if(redoPool.size()==Globals.poolSize)redoPool.remove(0);
            redoPool.push(obj);
            ide_methods.setRedoButtonsEnable(true);
        }
        logger.debug("**REDO**: "+action+"**"+firstIndex+"**"+lastIndex);
    }

    //Undo and Redo
    public void undo(boolean undo) throws Exception{
        pool = (undo)?undoPool:redoPool;       
        if(!pool.isEmpty()){
            Object obj[] = pool.pop();
            int action = Integer.parseInt(obj[0].toString()) * -1; //Invert the action
            int first = Integer.parseInt(obj[1].toString());
            int last = Integer.parseInt(obj[2].toString());
            Object[][] data = (Object[][]) obj[3];
            this.executingUndo = undo;
            this.executingRedo = !undo;
            switch(action){
                case ACTIVE:
                    this.setRowSelection(first, last);  //Select the row wich was deactivated
                    this.activateRows();                //activate the row;
                break;
                case DEACTIVE:
                    this.setRowSelection(first, last);  //Select the row who was activated
                    this.deactivateRows();              //deactivate the row;
                break;
                case CLEAR:
                    this.setRowSelection(first, last);  //Select the row who was inserted
                    this.clearRows();                   //remove the row;
                break;
                case RESTORE:
                    for(int i=0, j=first; j<=last ;j++,i++){
                        this.model.setRowValues(j, data[i]);
                    }
                    this.setRowSelection(first, last);
                    this.setInUndoPool(action, first, last, data);
                break;
                case INSERT:
                    this.insertRows(first,data,INSERT);
                break;
                case DELETE:
                    this.setRowSelection(first, last);  //Select the row which was inserted
                    this.deleteRow();                   //remove the row
                break;
                case MOVE_UP:
                    this.setRowSelection(first, last);
                    this.moveUp();
                break;
                case MOVE_DOWN:
                    this.setRowSelection(first, last);
                    this.moveDown();
                break;  
                case MOVE_FIRST:
                    this.setRowSelection(first, last);
                    this.moveFirst();
                break;  
                case RETURN1:
                    this.setRowSelection(0, last-first);
                    this.delete();
                    this.insertRows(first, data,RETURN1);
                break;  
                case MOVE_LAST:
                    this.setRowSelection(first, last);
                    this.moveLast();;
                break;  
                case RETURN2:
                    this.setRowSelection(model.getRowCount()-(last-first+1), model.getRowCount()-1);
                    this.delete();
                    this.insertRows(first, data,RETURN2);
                break;  

            }
            this.executingUndo = false;
            this.executingRedo = false;
        }
        if(this.undoPool.isEmpty()){
            ide_methods.setUndoButtonsEnable(false);
        }
        if(this.redoPool.isEmpty()){
            ide_methods.setRedoButtonsEnable(false);
        }
    }

    public void redo() {
        throw new UnsupportedOperationException("Not yet implemented");
    }
    
    /*********************************************************************
     ****************** C O N S T R U C T O R ****************************
     *********************************************************************/
    public TablePanel() {
        initComponents();
        commetsWin = new GlobalComments(this);
        model = new SimTableModel(new Object [][] {                             //My Model for the Table
                {new Boolean(true), null, null, null, null} },str_columNames);
        simulationTable.setModel(model);                                                //Set the model to the Table
        setLabels();
        setColumnsSize();                                                       //Resize the columns
        mss_message = MessagesService.getInstance();                                   //Obtaing instance for MessagesService class
        validations = ValidationService.getInstance();
        globalComments="";                                                      //To save the file's global comments
        ide_methods.setStatus(0,0);                                             //Set values to Line & Block in status bar
        simulationTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); //Aply Single Interval Selection
        lsm_list = simulationTable.getSelectionModel();                            //Obtain the selection model
        lsm_list.addListSelectionListener(new ListSelectionListener() {         //Add event to Selection model, to detect the change of row
               public void valueChanged(ListSelectionEvent e){
                     rowSelectionChanged();
               }
         });
        //Undo&Redo Pool
        undoPool = new Stack();
        redoPool = new Stack();
        simulationTable.setColumnSelectionInterval(Globals.BLOCK_COL, Globals.BLOCK_COL);
    }
    
    /*********************************************************************
     ********************** M E T H O D S ********************************
     *********************************************************************/
    
    /** Load the labels for Menu Items in the pop up Menu */
    public void setLabels(){ 
        jItemActivate.setText(Globals.language.getMessage("label.item.active"));
        jItemClear.setText(Globals.language.getMessage("label.item.clear"));
        jItemCopy.setText(Globals.language.getMessage("label.item.copy"));
        jItemCut.setText(Globals.language.getMessage("label.item.cut"));
        jItemDeactivate.setText(Globals.language.getMessage("label.item.deactive"));
        jItemDelete.setText(Globals.language.getMessage("label.item.delete"));
        jItemInsertAbove.setText(Globals.language.getMessage("label.item.above"));
        jItemInsertBellow.setText(Globals.language.getMessage("label.item.bellow"));
        jItemDown.setText(Globals.language.getMessage("label.item.down"));
        jItemFirst.setText(Globals.language.getMessage("label.item.first"));
        jItemLast.setText(Globals.language.getMessage("label.item.last"));
        jItemUp.setText(Globals.language.getMessage("label.item.up"));
        jItemPaste.setText(Globals.language.getMessage("label.item.paste"));
        commetsWin.setLabels(getFileName());
        str_columNames = new String[]{
                            Globals.language.getMessage("label.col.active"),
                            Globals.language.getMessage("label.col.label"),
                            Globals.language.getMessage("label.col.block"),
                            Globals.language.getMessage("label.col.value"),
                            Globals.language.getMessage("label.col.comment")};
        model.setColumnIdentifiers(str_columNames);
    }    

     public void setGlobalComments(String comments,boolean saved) {
        globalComments = comments;
        setSaved(saved);
    }

     public String getGlobalComments(){
        return this.globalComments.trim();
     }

    /**
     *  Method used to resize the columns width
     *  according to my preferences.
     */
    public void setColumnsSize() {
        simulationTable.setRowHeight(ROW_HEIGHT);                                       //Height of all Rows

        tbc_column = simulationTable.getColumnModel().getColumn(Globals.ACTIVATION_COL);
        tbc_column.setPreferredWidth(50);
        tbc_column.setMaxWidth(50);        
        tbc_column = simulationTable.getColumnModel().getColumn(Globals.LABEL_COL);
        tbc_column.setPreferredWidth(150);
        tbc_column.setMaxWidth(400);
        tbc_column = simulationTable.getColumnModel().getColumn(Globals.BLOCK_COL);
        tbc_column.setPreferredWidth(150);
        tbc_column.setMaxWidth(150);
    }

    /** 
     * Get the model
     * @return SimTableModel
     */
    public SimTableModel getModel() {
        return this.model;
    }
   /**
     * Add a new row even between other rows.
     * @param firstIndex = Row Index to start the insertion
     * @param number = Number of rows to insert
     */
    public void addMiddleRows(int firstIndex, int number){
        if(firstIndex >= 0){
            SimTableModel newModel = new SimTableModel(null,str_columNames); //Create a New Model
            for(int i=0;i<model.getRowCount();i++){                          
                if(i==firstIndex){      //if we are in the index who need to insert
                    while(number-- > 0) //insert the number of new rows required
                        newModel.addRow(obj_newRow);
                }
                newModel.addRow(model.getRow(i));   //add the other rows
            }
            model = newModel;
            simulationTable.setModel(model);
            newModel = null;
        }
        this.setColumnsSize();
    }


    /** This method is used to determine if a number
     * of rows give it by the user is  correct. The number
     * must be greater than MIN_ROWS_TO_INSERT and less than constant MAX_ROWS_TO_INSERT
     * @param  String provided by the user
     * @return -1 if the number is not correct
     * @return the number of rows if is correct
     */
    public int isValidNumberToInsert(String number){
        if(number==null)
            return 0;
        int rows;
        try{
            rows = Integer.parseInt(number);
            if(rows>=MIN_ROWS_TO_INSERT && rows<=MAX_ROWS_TO_INSERT)
                return rows;
            else
                return -1;
        }catch(Exception ex){
            logger.error(ex.getMessage());
            return -1;
        }
    }

    //Numbers > 0
    public int isValidNumber(String number){
        if(number==null)
            return 0;
        int numb;
        try{
            numb = Integer.parseInt(number);
            if(numb>0)
                return numb;
            else
                return -1;
        }catch(Exception ex){
            logger.error(ex.getMessage());
            return -1;
        }
    }

    /** add a row above the selected row:
     *  1. Obtain the selection
     *  2. We jut want the top row
     *  3. Add 1 row in the index ( the index was the position of the selected row
     *  4. The row will be inserted, and the other row who we replace is moved un position down*/
    public void addRowAbove() throws Exception{
        int index = lsm_list.getMinSelectionIndex(); //Obtain the top row
        if(index>=0){
            this.addMiddleRows(index,1);
            this.setSaved(false);
            this.setInUndoPool(INSERT,index,index,new Object[][]{obj_newRow});
            this.setRowSelection(index, index);
        }
    }

    /** add a row above the selected row:
     *  1. Obtain the selection
     *  2. We jut want the last row
     *  3. Add 1 row in the index + 1 ( the index was the position of the selected row
     *  4. The row will be inserted after the selected row. All rows that was bellow the
     *     selected row, will be placed after the new row.*/
    public void addRowBellow() throws Exception{
        int max =  lsm_list.getMaxSelectionIndex();
        int index = max+1;
        if(max>=0){
            if(index==this.model.getRowCount()){
                model.addRow(obj_newRow);
            }else
                this.addMiddleRows(index,1);
            this.setSaved(false);
            this.setInUndoPool(INSERT,index,index,new Object[][]{obj_newRow});
            this.setRowSelection(index, index);
        }  
    }

    /** like addRowAbove, but asking for a number of rows to add*/
    public void addRowsAbove(){
        int index = lsm_list.getMinSelectionIndex(); //Obtain the top row
        if(index>=0){
            String number;
            //Ask for number of rows
            do{
                number = this.mss_message.showInputMessage( this,Globals.language.getMessage("message.rows.insert")+" (Min: "+MIN_ROWS_TO_INSERT+", Max: "+MAX_ROWS_TO_INSERT+")",
                        null,Globals.config.getProperty("insert.default"));
            }while( isValidNumberToInsert(number) == -1);
            if(number == null)
                return;
            int numb = Integer.parseInt(number);
            this.addMiddleRows(index,numb);
            this.setSaved(false);
            Object objects[][] = new Object[numb][Globals.TOTAL_COLUMNS];
            for(int i=0;i<numb;i++){
                objects[i]= obj_newRow;
            }
            this.setInUndoPool(INSERT,index,(index-1)+numb,objects);
            this.setRowSelection(index, (index-1)+numb);
        }
    }

    /** like addRowBellow, but asking for a number of rows to add*/
    public void addRowsBellow() throws Exception{
        int index = lsm_list.getMaxSelectionIndex()+1; //Obtain the last row
        //Ask for number of rows
        if(index>0){
            String number;
            //mss_message = new MessagesService();
            do{
                number = this.mss_message.showInputMessage( this,Globals.language.getMessage("message.rows.insert")+" (Min: "+MIN_ROWS_TO_INSERT+", Max: "+MAX_ROWS_TO_INSERT+")",
                        null,Globals.config.getProperty("insert.default"));
            }while( isValidNumberToInsert(number) == -1);
            if(number == null)
                return;
            int numb = Integer.parseInt(number);
            if(index==this.model.getRowCount()){
                while(numb-->0)
                    model.addRow(obj_newRow);
            }else
                this.addMiddleRows(index,numb);
            //simulationTable.setRowSelectionInterval(index,index);
            this.setSaved(false);
            numb = Integer.parseInt(number);
            Object objects[][] = new Object[numb][Globals.TOTAL_COLUMNS];
            for(int i=0;i<numb;i++){
                objects[i]= obj_newRow;
            }
            this.setInUndoPool(INSERT,index,(index-1)+Integer.parseInt(number),objects);
            this.setRowSelection(index, (index-1)+Integer.parseInt(number));
        }
    }

    /** Internal Use 
     * Delete selected rows.
     * 1. Obtain the selection
     * 2. Remove every row selected
     */
    private void delete() throws Exception{
        int min = lsm_list.getMinSelectionIndex();  //first row slected
        int max = lsm_list.getMaxSelectionIndex();  //last row slected
        if(min >= 0 && max>=0 ){
            int number = max - min;                 //number of rows to delete
            if(number == model.getRowCount()-1)     //yo can't delete all rows
                number--;
            while(number-->=0)                      //by each row to delete
                this.model.removeRow(min);          //Delete the top row in selection
            //this.setColumnsSize();                  //restore columns size
            this.setSaved(false);                   //Change file status
        }
    }
    
    /** From IDE */
    public void deleteRow() throws Exception{
        int min = lsm_list.getMinSelectionIndex();  //first row slected
        int max = lsm_list.getMaxSelectionIndex();  //last row slected
        int rowCount = model.getRowCount();
        if(min >= 0 && max>=0 && rowCount > 1){
            int number = max-min+1;
            //First save the rows
            Object objects[][] = new Object[number][Globals.TOTAL_COLUMNS];
            for(int i=0;i<number;i++)
                objects[i]= model.getRow(min+i);
            this.setInUndoPool(DELETE,min,max,objects);
            //Now delete
            this.delete();
            //Aply selection after delete
            min = (min >= rowCount-number)?min-1:min;
            this.setRowSelection(min, min);
        }
    }

    /** Clear selected rows.
     * 1. Obtain the selection
     * 2. For every row selected
     *  3. Restore default values
     */
    public void clearRows() throws Exception{
        int min = lsm_list.getMinSelectionIndex();
        int max = lsm_list.getMaxSelectionIndex();
        int row = min;
        if(row>=0){
            int number = max-min+1;
            //First save the rows
            Object objects[][] = new Object[number][Globals.TOTAL_COLUMNS];
            for(int i=0;i<number;i++)
                objects[i]= model.getRow(min+i);
            this.setInUndoPool(CLEAR,min,max,objects);
            //Now Clear
            while(row<=max){
                this.model.setValueAt(true, row, 0);
                this.model.setValueAt("", row, 1);
                this.model.setValueAt("", row, 2);
                this.model.setValueAt("", row, 3);
                this.model.setValueAt("", row, 4);
                row++;
            }
            //this.setColumnsSize();
            this.setSaved(false);
        }
    }

    /**Force to active all rows*/
    public void activateRows() throws Exception{
        int min = lsm_list.getMinSelectionIndex();
        int max = lsm_list.getMaxSelectionIndex();
        int row = min;
        if(row>=0){
            while(row<=lsm_list.getMaxSelectionIndex()){
                this.model.setValueAt(true, row, 0);
                row++;
            }
            //this.setColumnsSize();
            this.setSaved(false);
            this.setInUndoPool(ACTIVE,min,max,null );
        }
    }

    /**Force to deactivate all rows*/
    public void deactivateRows() throws Exception{
        int min = lsm_list.getMinSelectionIndex();
        int max = lsm_list.getMaxSelectionIndex();
        int row = min;
        if(row>=0){
            while(row<=lsm_list.getMaxSelectionIndex()){
                this.model.setValueAt(false, row, 0);
                row++;
            }
            //this.setColumnsSize();
            this.setSaved(false);
            this.setInUndoPool(DEACTIVE,min,max,null );
        }
    }

    /**Take the actual status of first row and Force to active o deactive all rows*/
    public void active_deactiveRows() throws Exception{
        int min = lsm_list.getMinSelectionIndex();
        boolean status = Boolean.parseBoolean(this.model.getRow(min)[0].toString());
        if(status)
            deactivateRows();
        else
            activateRows();
    }
    
    /** Ask for a number of row and select it*/
    public void goToRowOrLabel() throws Exception{
        String destiny = mss_message.showInputMessage( 
                this,Globals.language.getMessage("message.rows.goto"),null,"1");      
        Integer numb = null;
        if(destiny != null){
            if(validations.isInteger(destiny)){
                numb = Integer.parseInt(destiny)-1;
                numb = (numb>=model.getRowCount())?model.getRowCount()-1:numb;
                setRowSelection(numb, numb);
            }else{
                numb = findRowByLabel(destiny);
                if(numb==null){
                    //Mensaje de label no encontrado
                    mss_message.showInformationMessage(this, Globals.language.getMessage("message.rows.goto.label"), null);
                }else{
                    setRowSelection(numb, numb);
                }
            }
        }
    }
    
    private Integer findRowByLabel(String destiny){
        int limit = model.getRowCount();
        Integer row = null;
        for(int index=0;index<limit;index++){
            String label = (model.getValueAt(index, Globals.LABEL_COL)==null)?"":
                    model.getValueAt(index, Globals.LABEL_COL).toString();
            if(label.equalsIgnoreCase(destiny)){
                row = index;
            }
        }
        return row;
    }

    /** Move all selected rows
     *  one position up.
     */
    public void moveUp() throws Exception{
        int row = lsm_list.getMinSelectionIndex();      //Obtain the first row
        int lastRow = lsm_list.getMaxSelectionIndex();  //Obtain the last row
        if(row>0){ //check: if we are in the first index
            Object ob[]=this.model.getRow(row-1);   //Save the values in the row above the selection
            if(lastRow == this.model.getRowCount()-1){
                this.model.addRow(ob);
            }else{
                this.obj_newRow = ob;                       //change the value of newRow with the saved data
                this.addMiddleRows(lastRow+1, 1);             //add The saved row
                this.restoreNewRow();                       //Restore original obj_newRow
            }
            this.model.removeRow(row-1);                //Remove the row above
            this.setSaved(false);
            this.setInUndoPool(MOVE_UP, row-1, lastRow-1, null);            
            this.setRowSelection(row-1, lastRow-1);
        }
    }

    /** Move all selected rows
     *  one position down.
     */
    public void moveDown() throws Exception{
        int row = lsm_list.getMinSelectionIndex();      //Obtain the first row
        int lastRow = lsm_list.getMaxSelectionIndex();  //Obtain the last row
        if(row>=0 && lastRow >= 0){
            if(lastRow != this.model.getRowCount()-1){      //check: if we are in the last index
                Object ob[]=this.model.getRow(lastRow+1);   //Save the values in the row above the selection
                this.obj_newRow = ob;                       //change the value of newRow with the saved data
                this.addMiddleRows(row, 1);                 //add The saved row
                this.restoreNewRow();                       //Restore original obj_newRow
                this.model.removeRow(lastRow+2);            //Remove the row above
                simulationTable.setRowSelectionInterval(row+1, row+1);
                this.setSaved(false);
                this.setInUndoPool(MOVE_DOWN, row+1, lastRow+1, null);
                this.setRowSelection(row+1, lastRow+1);
            }
        }
    }

    /** Move all selected rows
     *  at the first position in the table.
     * 1. Obtain selection
     * 2. Save rows information in an Object[][]
     * 3. Delete selected rows
     * 4. Add every row in the index 0 of the model
     */
    public void moveFirst() throws Exception{
        int row = lsm_list.getMinSelectionIndex();      //Obtain the first row
        int lastRow = lsm_list.getMaxSelectionIndex();  //Obtain the last row
        if(row>0 && lastRow >= 0){
            Object [][] saved_backup = IdeMethods.saved_rows ;     //backup original saved rows
            this.copyRows();                            //Save selected rows
            this.setInUndoPool(MOVE_FIRST,row, lastRow, IdeMethods.saved_rows);
            this.delete();                          //Delete selected rows
            this.addMiddleRows(0,lastRow-row+1);        //Add the rows at the first
            for(int i=0;i<IdeMethods.saved_rows.length;i++){
                this.model.setRowValues(i, IdeMethods.saved_rows[i]);
            }
            //Restore saved_rows
            IdeMethods.saved_rows = saved_backup;
            simulationTable.setRowSelectionInterval(0, 0);
            this.setSaved(false);
            //this.setInUndoPool(MOVE_FIRST, row+1, lastRow+1, null);
            this.setRowSelection(0, lastRow-row );
        }
    }

    /** Obtain the selection and move all selected rows
     *  in the end of the model.
     * 1. Obtain selection
     * 2. Save rows information in an Object[][]
     * 3. Delete selected rows
     * 4. Add every row in the end of the model
     */
    public void moveLast() throws Exception{
        //moveFirst(); //This is not required, but is used to improve a bad SWING behavior
        int row = lsm_list.getMinSelectionIndex();      //Obtain the first row
        int lastRow = lsm_list.getMaxSelectionIndex();  //Obtain the last row
        if(row>=0 && lastRow >= 0 && lastRow != model.getRowCount()-1 ){
            Object [][] saved_backup = IdeMethods.saved_rows ;     //backup original saved rows
            this.copyRows();                            //Save selected rows
            this.setInUndoPool(MOVE_LAST, row, lastRow, IdeMethods.saved_rows);
            this.delete();                          //Delete selected rows
           //Add the rows in the end
            for(int i=0;i<IdeMethods.saved_rows.length;i++){
                obj_newRow=(IdeMethods.saved_rows[i]);
                model.addRow(obj_newRow);
            }
            //Restore saved_rows
            IdeMethods.saved_rows = saved_backup;
            //Restore obj_newRow
            this.restoreNewRow();
            this.setSaved(false);
            //this.setInUndoPool(obj_newRow, str_path);
            this.setRowSelection( model.getRowCount()-(lastRow-row)-1, model.getRowCount()-1 );
        }
    }

    /** To restore the original value of a new Row*/
    public void restoreNewRow(){
        obj_newRow = new Object [] {
                     new Boolean(true), null , null, null, null }; //new Row
    }

    /** Save all the selected Rows, to paste them later
     */
    
    public void copyRows(){
        int row = lsm_list.getMinSelectionIndex();      //Obtain the first row
        int lastRow = lsm_list.getMaxSelectionIndex();  //Obtain the last row
        if(row>=0 && lastRow>=0){
             //Save all rows' information
            IdeMethods.saved_rows = new Object[ lastRow-row+1 ][5];
            for(int i=0, r=row;i<IdeMethods.saved_rows.length;i++,r++)
                IdeMethods.saved_rows[i]= this.model.getRow(r);
            this.setSaved(false);
        }
    }

    /** Save all the selected Rows, to paste them later
     *  but remove them too.     */
    public void cutRows() throws Exception{
        int min = lsm_list.getMinSelectionIndex();  //Obtain the first row
        int max = lsm_list.getMaxSelectionIndex();  //Obtain the last row
        int rowCount = model.getRowCount();
        if(min >= 0 && max>=0 && rowCount > 1){
            this.copyRows();    //Copy selected rows
            this.deleteRow();  //Delete selected rows
            this.setSaved(false);
        }
    }

    public void pasteRows(){
        if(IdeMethods.saved_rows != null){ //Just paste when something is already copy/cut
            int selection = lsm_list.getMaxSelectionIndex()+1;
            if(selection>0){
                if(selection == this.model.getRowCount()){
                    for(int i=0;i<IdeMethods.saved_rows.length;i++){
                       this.obj_newRow = IdeMethods.saved_rows[i];
                       this.model.addRow(obj_newRow);
                    }
                }else{
                    for(int i=0;i<IdeMethods.saved_rows.length;i++){
                        this.obj_newRow = IdeMethods.saved_rows[i];
                        this.addMiddleRows(selection+i, 1);
                    }
                }
                this.restoreNewRow();
                this.setSaved(false);
                this.setInUndoPool(INSERT, selection, selection + IdeMethods.saved_rows.length -1, IdeMethods.saved_rows); //Save the original position, before the movement
                this.setRowSelection( selection , selection + IdeMethods.saved_rows.length -1 );
            }
        }
    }

    public int indexImport=-1;
    public void importRows(ArrayList<Object[]> list){
        Object [][] obj = new Object[list.size()][5];
        if(indexImport == -1){
            indexImport = lsm_list.getMaxSelectionIndex()+1;  //Obtain the last row.
        }
        if(indexImport == this.model.getRowCount()){
            for(int i=0;i<list.size();i++){
               this.obj_newRow = list.get(i);
               obj[i]=obj_newRow;
               this.model.addRow(obj_newRow);
            }
        }else{
            int index = indexImport;
            for(int i=0;i<list.size();i++){
               this.obj_newRow = list.get(i);
               obj[i]=obj_newRow;
               this.addMiddleRows(index++, 1);
            }
        }
        this.setSaved(false);
        this.setInUndoPool(INSERT, indexImport, indexImport + list.size()-1, obj); //Save the original position, before the movement
        this.setRowSelection( indexImport , indexImport +  list.size()-1 );
    }

    public void insertRows(int first, Object [][] obj, int action){
        
        if(first == this.model.getRowCount()){
            for(int i=0;i<obj.length;i++){
               this.obj_newRow = obj[i];
               this.model.addRow(obj_newRow);
            }
        }else{
            int index = first;
            for(int i=0;i<obj.length;i++){
               this.obj_newRow = obj[i];
               obj[i]=obj_newRow;
               this.addMiddleRows(index++, 1);
            }
        }
        this.restoreNewRow();
        //this.setSaved(false);
        this.setInUndoPool(action, first, first +  obj.length-1 , obj); //Save the original position, before the movement
        this.setRowSelection( first , first +  obj.length-1 );
    }

    /** Select all row in the model*/
    public void selectAll(){
        simulationTable.selectAll();
    }
    
    public void setCellSelection(boolean cell) {
        simulationTable.setCellSelectionEnabled(cell);
        simulationTable.setColumnSelectionAllowed(cell);
        if(!cell){
            simulationTable.setRowSelectionAllowed(true);
            simulationTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
        }
    }
    
    public void setRowSelection(int index1, int index2){
        if(simulationTable.getCellSelectionEnabled()){
            simulationTable.setColumnSelectionInterval(0, Globals.TOTAL_COLUMNS-1);
            simulationTable.setRowSelectionInterval(index1, index2);
        }else{
            lsm_list.setSelectionInterval(index1, index2);
        }
    }


    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPopupMenu = new javax.swing.JPopupMenu();
        jItemActivate = new javax.swing.JMenuItem();
        jItemDeactivate = new javax.swing.JMenuItem();
        jItemDelete = new javax.swing.JMenuItem();
        jItemInsertAbove = new javax.swing.JMenuItem();
        jItemInsertBellow = new javax.swing.JMenuItem();
        jItemClear = new javax.swing.JMenuItem();
        jItemFirst = new javax.swing.JMenuItem();
        jItemLast = new javax.swing.JMenuItem();
        jItemUp = new javax.swing.JMenuItem();
        jItemDown = new javax.swing.JMenuItem();
        jItemCopy = new javax.swing.JMenuItem();
        jItemCut = new javax.swing.JMenuItem();
        jItemPaste = new javax.swing.JMenuItem();
        jScrollPane = new javax.swing.JScrollPane();
        simulationTable = new javax.swing.JTable();

        jItemActivate.setText("Activate Rows");
        jItemActivate.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemActivateActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemActivate);

        jItemDeactivate.setText("Deactivate Rows");
        jItemDeactivate.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemDeactivateActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemDeactivate);

        jItemDelete.setText("Delete Rows");
        jItemDelete.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemDeleteActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemDelete);

        jItemInsertAbove.setText("Insert Rows Above");
        jItemInsertAbove.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemInsertAboveActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemInsertAbove);

        jItemInsertBellow.setText("Insert Rows Below");
        jItemInsertBellow.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemInsertBellowActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemInsertBellow);

        jItemClear.setText("Clear Rows");
        jItemClear.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemClearActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemClear);

        jItemFirst.setText("Move First");
        jItemFirst.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemFirstActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemFirst);

        jItemLast.setText("Move Last");
        jItemLast.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemLastActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemLast);

        jItemUp.setText("Move Up");
        jItemUp.setAutoscrolls(true);
        jItemUp.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemUpActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemUp);

        jItemDown.setText("Move Down");
        jItemDown.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemDownActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemDown);

        jItemCopy.setText("Copy Rows");
        jItemCopy.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemCopyActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemCopy);

        jItemCut.setText("Cut Rows");
        jItemCut.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemCutActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemCut);

        jItemPaste.setText("Paste Rows");
        jItemPaste.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jItemPasteActionPerformed(evt);
            }
        });
        jPopupMenu.add(jItemPaste);

        simulationTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {new Boolean(true), null, null, null, null}
            },
            new String [] {
                "active", "label", "block", "values", "comments"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.Boolean.class, java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.String.class
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }
        });
        simulationTable.setCellSelectionEnabled(true);
        simulationTable.setComponentPopupMenu(jPopupMenu);
        simulationTable.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                simulationTableKeyPressed(evt);
            }
        });
        jScrollPane.setViewportView(simulationTable);

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jScrollPane, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 464, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jScrollPane, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 299, Short.MAX_VALUE)
        );
    }// </editor-fold>//GEN-END:initComponents

 
    /*********************************************************************
     ******************* KEY PRESSED ACTIONS *****************************
     *********************************************************************/

    //KeyCode
    
    private final int DEL_CODE     = 8;
    private final int TAB_CODE     = 9;
    private final int ENTER_CODE   = 10;
    private final int SHIFT_CODE   = 16;
    private final int ESC_CODE     = 27;
    private final int LEFT_CODE    = 37;
    private final int UP_CODE      = 38;
    private final int RIGHT_CODE   = 39;
    private final int DOWN_CODE    = 40;
    private final int SUPR_CODE    = 127;
    private final int GENERIC_CODE = 65535;

    private final int FIRST_COLUMN = 0;
    private IdeMethods ide_methods = IdeMethods.getCreatedInstance();
    //private final int LAST_COLUMN = 4;
    //private final int BLOCK_COLUMN = 2;
    
    private void simulationTableKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_simulationTableKeyPressed
        try{
        /************* Add a new Last Row
         *Only when we are in the last row and press ENTER in any column
         **************/
        int rowCount = simulationTable.getRowCount();
        int selectedRow = simulationTable.getSelectedRow();
        if( selectedRow == rowCount-1   //if we are in the last row
             &&  evt.getKeyCode() == ENTER_CODE ){                          //And press Enter
            model.addRow(obj_newRow);
            this.setInUndoPool(INSERT, rowCount,rowCount,new Object[][]{obj_newRow});
            simulationTable.setColumnSelectionInterval(Globals.BLOCK_COL, Globals.BLOCK_COL);
            //Globals.console("**** Last Row Added ****");
        }
        /************* Delete selected Row
         *Only when we are in the first column and press DEL in any row
         *The first Row couldn't be deleted
         **************/
        else if (simulationTable.getSelectedColumn() == FIRST_COLUMN               //if we are in the fisrt column, no matter what row
                  && ( evt.getKeyCode() == DEL_CODE ||                     //And press DELETE
                       evt.getKeyCode() == SUPR_CODE ) ){                   //oR press SUPR
            if(model.getRowCount() > 1){
                Object [] row = model.getRow(selectedRow);
                model.removeRow( selectedRow );                                         //remove the row
                this.setInUndoPool(DELETE, selectedRow,selectedRow,new Object[][]{row});
            }
            if(selectedRow != 0)
                evt.setKeyCode( DOWN_CODE );
            else
                evt.setKeyCode( UP_CODE );
             //Globals.console("**** Row Deleted ****");
        }
        else if(evt.getKeyCode() == ENTER_CODE  ){
            simulationTable.setColumnSelectionInterval(Globals.BLOCK_COL, Globals.BLOCK_COL);
        }
        /************* Mark File as Modified
         *When Tipped another character different from TAB,ENTER, or MOVE, etc
         **************/
        else if ((int)evt.getKeyChar() != GENERIC_CODE &&
                      evt.getKeyCode() != TAB_CODE     &&
                      evt.getKeyCode() != SHIFT_CODE   &&
                      evt.getKeyCode() != ESC_CODE     &&
                      evt.getKeyCode() != UP_CODE      &&
                      evt.getKeyCode() != DOWN_CODE    &&
                      evt.getKeyCode() != LEFT_CODE    &&
                      evt.getKeyCode() != RIGHT_CODE  
                ){
            this.setSaved(false); //Because one cell has benn modified
            //Save the row in the Pool of Undo&Redo
            //setInUndoPool( this.model.getRow(simulationTable.getSelectedRow()), "");
            //Globals.console(row.toString());
        }
   
      }catch(Exception ex){
          logger.error(ex.getMessage());
      }
      //Globals.console("**** KeyChar= "+(int)evt.getKeyChar());
      //Globals.console("**** KeyCode= "+(int)evt.getKeyCode());
      //Globals.console("**** KeyModifier= "+(int)evt.getModifiersEx());
      
    }//GEN-LAST:event_simulationTableKeyPressed

    /*********************************************************************
     ******************* POP MENU ACTIONS ********************************
     *********************************************************************/

    private void jItemInsertAboveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemInsertAboveActionPerformed
        try{
            this.addRowsAbove();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemInsertAboveActionPerformed

    private void jItemInsertBellowActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemInsertBellowActionPerformed
        try{
            this.addRowsBellow();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemInsertBellowActionPerformed

    private void jItemClearActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemClearActionPerformed
        try{
            this.clearRows();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemClearActionPerformed

    private void jItemDeleteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemDeleteActionPerformed
        try{
            this.deleteRow();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemDeleteActionPerformed

    private void jItemActivateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemActivateActionPerformed
        try{
            this.activateRows();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemActivateActionPerformed

    private void jItemDeactivateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemDeactivateActionPerformed
        try{
            this.deactivateRows();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemDeactivateActionPerformed

    private void jItemUpActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemUpActionPerformed
        try{
            this.moveUp();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemUpActionPerformed

    private void jItemDownActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemDownActionPerformed
        try{
            this.moveDown();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemDownActionPerformed

    private void jItemFirstActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemFirstActionPerformed
        try{
            this.moveFirst();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemFirstActionPerformed

    private void jItemLastActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemLastActionPerformed
        try{
            this.moveLast();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemLastActionPerformed

    private void jItemCopyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemCopyActionPerformed
        try{
            this.copyRows();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemCopyActionPerformed

    private void jItemCutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemCutActionPerformed
        try{
            this.cutRows();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemCutActionPerformed

    private void jItemPasteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jItemPasteActionPerformed
        try{
            this.pasteRows();
        }catch(Exception ex){
            logger.error(ex.getMessage());
        }
}//GEN-LAST:event_jItemPasteActionPerformed


private void rowSelectionChanged() {
    try {       
        int line_counter  = simulationTable.getSelectedRow();
        //Globals.console("***********ROW CHANGED*****"+line_counter);
        int block_counter = model.getBlockCounter(line_counter);
        ide_methods.setStatus(line_counter+1,block_counter);
    } catch (Exception ex) {
        logger.error(ex.getMessage());
    }
}

public GlobalComments getCommentsWindow() {
    this.commetsWin.setText(globalComments);
    this.commetsWin.setLabels(getFileName());
    return commetsWin;
}


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenuItem jItemActivate;
    private javax.swing.JMenuItem jItemClear;
    private javax.swing.JMenuItem jItemCopy;
    private javax.swing.JMenuItem jItemCut;
    private javax.swing.JMenuItem jItemDeactivate;
    private javax.swing.JMenuItem jItemDelete;
    private javax.swing.JMenuItem jItemDown;
    private javax.swing.JMenuItem jItemFirst;
    private javax.swing.JMenuItem jItemInsertAbove;
    private javax.swing.JMenuItem jItemInsertBellow;
    private javax.swing.JMenuItem jItemLast;
    private javax.swing.JMenuItem jItemPaste;
    private javax.swing.JMenuItem jItemUp;
    private javax.swing.JPopupMenu jPopupMenu;
    private javax.swing.JScrollPane jScrollPane;
    private javax.swing.JTable simulationTable;
    // End of variables declaration//GEN-END:variables


    /*********************************************************************
     ******************* FROM SAVED INTERFACE ****************************
     *********************************************************************/


    private boolean boo_saved;
    private boolean boo_new;
    private String  str_fileName;
    private String  str_path;

    public boolean isSaved() {
        return this.boo_saved;
    }

    public void setFileName(String name) {
        this.str_fileName = name;
    }

    public void setSaved(boolean saved) {
        this.boo_saved = saved;
    }

    public String getFileName() {
        return this.str_fileName;
    }

    public void setPath(String path) {
        this.str_path = path;
    }

    public String getAbsolutePath() {
        return this.str_path+this.str_fileName;
    }

    public String getPath() {
        return this.str_path;
    }

    public void setNew(boolean isNew) {
        this.boo_new = isNew;
    }

    public boolean isNew() {
        return this.boo_new;
    }


}
